<template>
  <div class="virtual-list-container" ref="container" @scroll="handleScroll">
    <div class="virtual-list-phantom" :style="{ height: totalHeight + 'px' }"></div>
    <div class="virtual-list" :style="{ transform: `translateY(${offset}px)` }">
      <div v-for="(item, index) in visibleData" :key="item.id" class="virtual-list-item" ref="items" :data-id="item.id">
        <!-- 你的列表项内容 -->
        <slot :item="item" :index="index"></slot>
      </div>
    </div>
  </div>
</template>

<script setup>

const props = defineProps({
  // 数组
  listData: {
    type: Array,
    required: true
  },
  // 每条数据的高度
  estimatedItemHeight: {
    type: Number,
    default: 100
  },
  // 缓冲数据量
  buffer: {
    type: Number,
    default: 5
  }
})

const visibleData = ref([]) // 数组
const offset = ref(0) // 偏移量
const startIndex = ref(0) // 页面可视区域的数组下标--开始
const endIndex = ref(0)// 页面可视区域的数组下标--结束
const positions = ref([]) // 存储每个元素的位置信息
const screenHeight = ref(0) // 可视区域总高度
const totalHeight = ref(0) // 列表总高度
const container = ref(null) // 可视区域容器
const items = ref(null) // 每条数据容器集合


//  计算隐藏区的起始索引
const updateVisibleData = () => {
  const scrollTop = container.value.scrollTop

  startIndex.value = getStartIndex(scrollTop)
  endIndex.value = getStartIndex(scrollTop + screenHeight.value)

  // 添加缓冲区
  startIndex.value = Math.max(0, startIndex.value - props.buffer)
  endIndex.value = Math.min(props.listData.length - 1, endIndex.value + props.buffer)

  // 需要展示的数组
  visibleData.value = props.listData.slice(startIndex.value, endIndex.value + 1)

  // 计算偏移量
  offset.value = startIndex.value > 0 ? positions.value[startIndex.value - 1].bottom : 0
}

// 查找第一个bottom大于scrollTop的item
const getStartIndex = (scrollTop = 0) => {
  // 使用二分查找法
  let left = 0
  let right = positions.value.length - 1
  let mid = null
  let current = null

  while (left <= right) {
    mid = Math.floor((left + right) / 2)
    current = positions.value[mid].bottom

    if (current === scrollTop) {
      return mid + 1
    } else if (current < scrollTop) {
      left = mid + 1
    } else {
      right = mid - 1
    }
  }

  return left
}

// 初始化每条数据的位置信息（根据预设值进行计算）
const initPositions = () => {
  positions.value = props.listData.map((item, index) => {
    return {
      index,
      height: props.estimatedItemHeight,
      top: index * props.estimatedItemHeight,
      bottom: (index + 1) * props.estimatedItemHeight
    }
  })

  totalHeight.value = positions.value[positions.value.length - 1]?.bottom || 0
}

const updateItemsSize = () => {
  console.log(items.value);

  const nodes = items.value
  if (!nodes || !nodes.length) return

  nodes.forEach(node => {
    const rect = node.getBoundingClientRect()
    const id = node.getAttribute('data-id')

    const index = props.listData.findIndex(item => item.id == id)


    if (index !== -1) {
      const oldHeight = positions.value[index].height
      const height = rect.height
      const dValue = height - oldHeight

      if (dValue) {
        positions.value[index].height = height
        positions.value[index].bottom = positions.value[index].top + height

        // 调整后续元素的位置
        for (let i = index + 1; i < positions.value.length; i++) {
          positions.value[i].top = positions.value[i - 1].bottom
          positions.value[i].bottom = positions.value[i].top + positions.value[i].height
        }

        totalHeight.value = positions.value[positions.value.length - 1].bottom
      }
    }
  })
  console.log(positions.value);
}


const handleScroll = () => {
  updateVisibleData()
  // 滚动后更新元素尺寸
  nextTick(() => {
    updateItemsSize()
  })
}

initPositions()


onMounted(() => {
  console.log(positions.value);

  screenHeight.value = container.value.clientHeight
  updateVisibleData()

  // 初始渲染后获取实际高度并更新位置信息
  nextTick(() => {
    updateItemsSize()
  })
})

watch(() => props.listData, () => {
  console.log('进入watch');
  console.log(props.listData);
  initPositions()
  updateVisibleData()
})


</script>

<style scoped>
.virtual-list-container {
  position: relative;
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.virtual-list-phantom {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  z-index: -1;
}

.virtual-list {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
}

.virtual-list-item {
  box-sizing: border-box;
  width: 100%;
}
</style>